package lambdasinaction.chap5.my;

import java.util.Optional;
import lambdasinaction.chap4.MyDish;

/**
 * 查找和匹配
 * 常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性
 * Stream API 通过 allMatch、anyMatch、noneMatch、findFirst 和findAny方法提供了这样的工具
 * allMatch、anyMatch、noneMatch都用到了我们所谓的短路，这就是大家熟悉的 Java中&&和||运算符短路在流中的版本
 * @author Fish
 * @since 0.0.1 2018/7/24 23:16
 */
public class MyFinding {

  public static void main(String[] args) {
    if (isVegetarianFriendlyMenu()) {
      System.out.println("Vegetarian friendly");
    }

    System.out.println(isHealthMenu());
    System.out.println(isHealthMenu2());

    // 返回一个 Optional<MyDish>
    Optional<MyDish> dish = findVegetarianDish();
    // 如果包含一个值就打印它，否则什么都不做
    dish.ifPresent(d -> System.out.println(d));


  }

  /**
   * 检查谓词是否至少匹配一个元素
   * anyMatch 方法可以回答“流中是否有一个元素能匹配给定的谓词”。
   * 比如，你可以用它来看看菜单里面是否有素食可以选择
   * @return
   */
  private static boolean isVegetarianFriendlyMenu() {
    return MyDish.menu.stream().anyMatch(MyDish::isVegetarian);
  }

  /**
   * 检查谓词是否匹配所有元素
   * allMatch方法的工作原理和 anyMatch类似，但它会看流中的元素是否都能匹配给定的谓词
   * 比如，你可以用它来看看菜品是否有利健康（即所有菜的热量都低于1000卡路里）
   * @return
   */
  private static boolean isHealthMenu() {
    return MyDish.menu.stream().allMatch(d -> d.getCalories() < 1000);
  }

  /**
   * 和 allMatch相对的是 noneMatch。它可以确保流中没有任何元素与给定的谓词匹配。
   * 比如，你可以用noneMatch重写前面的例子
   * @return
   */
  private static boolean isHealthMenu2() {
    return MyDish.menu.stream().noneMatch(d -> d.getCalories() >= 1000);
  }

  /**
   * 查找元素
   * findAny方法将返回当前流中的任意元素，它可以与其他流操作结合使用。
   * 比如，你可能想找到一个素食菜肴，你可以结合filter 和findAny方法来实现这个查询
   * 流水线将在后台进行优化使其只需走一遍，并在利用短路找到结果时立即结束
   * @return Optional<T>类是一个容器类，代表一个值存在或不存在
   * Java 8的库设计人员引入了Optional<T>，这样就不用返回众所周知的容易出问题的 null 了
   * Optional里面有几种可以迫使你显示的检查值是否存在或处理值不存在的情形的方法也不错
   * isPresent()将在 Optional包含值的时候返回 true，否则返回 false
   * ifPresent(Consumer<T> block)会在值存在的时候执行给定的代码块
   *  Consumer函数式接口，它让你接受一个 T 类型参数，并返回 void 的Lambda 表达式
   * T get() 会在值存在时返回值，否则抛出一个 NoSuchElement异常
   * T orElse(T other)会在值存在时返回一个值，否则返回一个默认值
   */
  private static Optional<MyDish> findVegetarianDish() {
    return MyDish.menu.stream().filter(MyDish::isVegetarian).findAny();
  }
}
